﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AtDevice
{
    /// <summary>
    /// Class to parse AT responses received from the device.
    /// </summary>
    class AtResponseParser
    {
        private bool _hasResult = false;
        private AtResponse _response = new AtResponse();

        /// <summary>
        /// Construct new parser.
        /// </summary>
        /// <param name="response">The response to parse.</param>
        public AtResponseParser()
        {
        }

        /// <summary>
        /// Handle errors and throw appropriate exception if needed.
        /// </summary>
        private void HandleErrors(string response)
        {
            if (response.StartsWith("ERROR"))
                throw new AtException();

            if (response.StartsWith("COMMAND NOT SUPPORT"))
                throw new CommandNotSupported();

            if (response.StartsWith("NO CARRIER"))
                throw new NoCarrierException();

            if (response.StartsWith("+CME ERROR"))
                throw new CmeException(GetErrorString(response));

            if (response.StartsWith("+CMC ERROR"))
                throw new CmsException(GetErrorNumber(response));
        }

        private string GetErrorString(string response)
        {
            // +CME ERROR: operation not allowed
            return response.Split(':')[1].Substring(1);
        }

        private int GetErrorNumber(string response)
        {
            // +CMS ERROR: 301
            return Int32.Parse(response.Split()[2]);
        }

        internal bool HasResult()
        {
            return _hasResult;
        }

        /// <summary>
        /// Parse the response. Checks if an error is received and if so, throws an appropriate exception.
        /// </summary>
        internal void ParseLine(string response)
        {
            HandleErrors(response);

            _hasResult = IsReturnValue(response);
            if (!_hasResult)
                ProcessLine(response);
        }

        private static bool IsReturnValue(string response)
        {
            // most commands return OK when successfull but when sending Ctrl+Z
            // terminated SMS the device returns a prompt "> "
            return response.StartsWith("OK")/* || response.StartsWith(">")*/;
        }

        /// <summary>
        /// Processes an actual response line. Checks if the line is in format "+XXX: n,n"
        /// and then appends new AtResponseItem to the response.
        /// If not, checks if this is first line received and creates plain text response
        /// or appends PDU data to the latest response item.
        /// </summary>
        /// <param name="response"></param>
        private void ProcessLine(string response)
        {
            // Most responses are in format: "+CSQ: 11,99"
            // but some are just plain text
            if (response.StartsWith("+"))
                ProcessAtResponse(response);
            else if(response.StartsWith("^"))
                ProcessSpecialResponse(response);
            else if (_response.Items.Count > 0)
                ProcessPduResponse(response);
            else
                ProcessPlainResponse(response);
        }

        private void ProcessAtResponse(string response)
        {
            _response.AddItem(new AtResponseItem(response));
        }

        private void ProcessSpecialResponse(string response)
        {
            _response.AddItem(new AtResponseItem(response, true));
        }

        private void ProcessPduResponse(string response)
        {
            _response.Items.Last().SetPduData(response);
        }

        private void ProcessPlainResponse(string response)
        {
            _response.SetPlainText(response);
        }

        internal AtResponse GetResponse()
        {
            return _response;
        }
    }
}
